/**
 * ContractServiceImpl.java
 * @author
 * 作者 屈甲康 
 */
package org.jackysoft.service.impl;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.NonUniqueResultException;
import org.hibernate.criterion.Property;
import org.jackysoft.entity.Contract;
import org.jackysoft.entity.ContractClient;
import org.jackysoft.entity.ContractDataItem;
import org.jackysoft.entity.ContractItem;
import org.jackysoft.entity.Department;
import org.jackysoft.entity.Product;
import org.jackysoft.entity.ProductComeoutItem;
import org.jackysoft.entity.ProductIncomeItem;
import org.jackysoft.entity.User;
import org.jackysoft.query.Query;
import org.jackysoft.report.ProductValidation;
import org.jackysoft.report.data.ContractImportor;
import org.jackysoft.service.AbstractStringIdServiceImpl;
import org.jackysoft.service.ContractItemService;
import org.jackysoft.service.ContractService;
import org.jackysoft.util.IOUtils;
import org.jackysoft.util.Page;
import org.jackysoft.util.SpringSecurityUtils;
import org.joda.time.DateTime;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;

@Service
@Transactional
public class ContractServiceImpl extends AbstractStringIdServiceImpl<Contract>
		implements ContractService {

	/**
	 * 
	 */

	private static final long serialVersionUID = -202971679709600597L;
	private static final Log logger = LogFactory
			.getLog(ContractServiceImpl.class);
	private ContractImportor dataImportor;

	public ContractServiceImpl() {
		super(Contract.class);
	}

	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
	public void remove(Contract t) throws DataAccessException {
		String hql = "delete from " + table(ProductIncomeItem.class)
				+ " where contract.id='" + t.getId() + "'";
		query(hql).executeUpdate();
		t = get(t.getId());
		Collection<ContractItem> items = t.getContractItems();
		for (ContractItem item : items) {
			hql = "delete from " + table(ProductIncomeItem.class)
					+ " where parentItem.id='" + item.getId() + "'";
			query(hql).executeUpdate();
		}
		hql = HQL("delete from %s t where t.contract.id='%s'",table(ProductComeoutItem.class),t.getId());
		query(hql).executeUpdate();
		logger.info("合同"+t.getId()+"已删除");
		super.remove(t);
	}

	@Override
	public void create(Contract bean) throws DataAccessException {
		updateValue(bean);
		super.create(bean);
		Collection<ContractItem> items = bean.getContractItems();
		for (ContractItem item : items) {
			if (item == null)
				continue;
			item.setContract(bean);
			item.setClient(bean.getClient());
			item.setFireDate(bean.getDueDate());
			item.setSecurity(bean.getSecurity());	
		}
	}

	@Override
	public String   create(File file, Integer year) {

		byte[] content = IOUtils.toByteArray(file);

		Map<String, Collection<ContractItem>> items = dataImportor.fromStream(new ByteArrayInputStream(content));
		
		Map<String, Collection<String>> missed = new HashMap<String,Collection<String>>(){
			
		    /**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			{
			
				put("产品错误",dataImportor.getMissItems());				
				put("客户错误",dataImportor.getMissClients());
		        put("编号错误",dataImportor.getMissNo());		        
		       
		        logger.info("System.exit(0) run here will be");
				//System.exit(0);
			}
		};		
		
		if(
				dataImportor.getMissClients().size()==0
				&&
				dataImportor.getMissItems().size()==0
				&&
				dataImportor.getMissNo().size()==0
				){
			  logger.info("数据完整，准备导入");
		      addContractItem(items,year);   
		      logger.info("导入完成!");  
		}
		
		Document doc = Jsoup.parse("<div class='alert alert-error'>出现错误，合同未导入</div>");
	    for(String key : missed.keySet()){
	    	Element head  = doc.appendElement("h4");
	    	head.html(key);
	    	Element ul = doc.appendElement("ul");
	    	for(String e:missed.get(key)){
	    		ul.appendElement("li").html(e);
	    	}
	    }
		
        return doc.outerHtml();
	}

	private void addContractItem(Map<String, Collection<ContractItem>> items,int year){
		for (Map.Entry<String, Collection<ContractItem>> entry : items
				.entrySet()) {
			Contract cont = getByName(entry.getKey());
			Collection<ContractItem> crtitems = entry.getValue();
			if (crtitems == null || crtitems.isEmpty())
				continue;
			if (cont == null) {
				ContractItem itm = crtitems.iterator().next();
				cont = new Contract();
				DateTime dt = DateTime.now();
				dt = dt.withYear(year);
				cont.setDueDate(dt);
				cont.setName(entry.getKey());
				cont.setClient(itm.getClient());

			}
			cont.getContractItems().addAll(crtitems);
			create(cont);
			logger.info("合同 " + cont.getName() + " 已经导入!");
		}
	}
	
	
	@Override
	@Transactional(readOnly = true)
	public Contract get(String id) throws DataAccessException {
		Contract bean = super.get(id);
		updateValue(bean);
		return bean;
	}

	private void updateValue(Contract bean) {
		Collection<ContractItem> citems = bean.getContractItems();
		Double am = 0D;

		for (ContractItem item : citems) {
			if (item == null)
				continue;
			Collection<ContractDataItem> ditems = item.getDataItems();
			Double total = 0D;
			for (ContractDataItem ditem : ditems) {
				total += ditem.getAssumpsit();
			}
			item.setAmount(total);
			am += item.getItemAmount();
		}
		bean.setAmount(am);

	}

	@Override
	public void updateDetail(ContractDataItem dataItem) {
		getSession().merge(dataItem);

	}

	@Override
	public void updateItem(ContractItem item) {
		ContractItem citem = (ContractItem) getSession().get(
				ContractItem.class, item.getId());
		citem.setValidation(item.getValidation());
		citem.setStorage(item.getStorage());
		citem.setRemark(item.getRemark());
	}

	@Override
	public void removeItem(Contract contract, ContractItem item) {
		contract = get(contract.getId());
		contract.getContractItems().remove(item);
	}

	@Resource
	public void setDataImportor(ContractImportor dataImportor) {
		this.dataImportor = dataImportor;
	}

	@Override
	public void submitLine(ProductIncomeItem pin) {
		User worker = SpringSecurityUtils.getCurrentUser();
		pin.setFireDate(DateTime.now());
		pin.setWorker(worker);
		getSession().save(pin);
		afterIncomeChanged(pin, true);

	}

	@Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Collection<ProductIncomeItem> showSubmitLine(Contract cont,
			ContractItem item) {

		String hql = "from " + table(ProductIncomeItem.class)
				+ " p where p.contract.id=:cont and p.owner.code=:code";
		List list = query(hql).setString("cont", cont.getId())
				.setString("code", item.getOwner().getCode()).list();
		return list;

	}

	@Override
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Collection<ProductIncomeItem> myIncomeItems(DateTime firedate) {

		if (firedate == null)
			firedate = DateTime.now();
		User owner = SpringSecurityUtils.getCurrentUser();
		String hql = String
				.format("from %s "
						+ " p where p.worker.username=:user and datediff(date(p.fireDate),date('%s'))=0"
						+ " order by p.fireDate desc",
						table(ProductIncomeItem.class),
						firedate.toString("yyyy-MM-dd"));
		List list = query(hql).setString("user", owner.getUsername()).list();
		return list;
	}

	@Override
	public Collection<Contract> findContractsByCode(Product pdt) {
		String hql = String.format(
				"from %s cc where cc.owner.code='%s' group by cc.contract",
				table(ContractItem.class), pdt.getCode());
		List<ContractItem> citems = query(hql).list();
		Collection<Contract> conts = Lists.newArrayList();
		for (ContractItem item : citems) {
			conts.add(item.getContract());
		}
		return conts;
	}

	@Override
	public void turnin(ProductIncomeItem income) {

		Product pdt = income.getOwner();
		Contract cont = income.getContract();
		String batchName = income.getBatchName();
		double value = income.getValue();
		cont = get(cont.getId());
		String hql = String.format(
				"from %s cc where cc.owner.code='%s' and cc.contract.id='%s'",
				table(ContractItem.class), pdt.getCode(), cont.getId());
		Object obj = null;
		try {
			obj = query(hql).uniqueResult();

		} catch (NonUniqueResultException ne) {
			logger.error(ne);
			return;
		}

		ContractItem citem = (ContractItem) obj;
		ProductIncomeItem pin = new ProductIncomeItem();
		pin.setContract(cont);
		pin.setParentItem(citem);
		pin.setOwner(pdt);
		pin.setBatchName(batchName);
		pin.setValue(value);
		this.submitLine(pin);
	}

	@Override
	public void removeIncome(String id) {
		ProductIncomeItem pin = (ProductIncomeItem) getSession().get(
				ProductIncomeItem.class, id);
		afterIncomeChanged(pin, false);
		String hql = String.format("delete from %s where id='%s'",
				table(ProductIncomeItem.class), id);
		query(hql).executeUpdate();

	}

	void afterIncomeChanged(ProductIncomeItem pin, boolean added) {
		if (pin == null)
			return;
		ContractItem citem = pin.getParentItem();
		citem = (ContractItem) getSession().get(ContractItem.class,
				citem.getId());
		int month = pin.getFireDate().getMonthOfYear();
		ContractDataItem ditem = null;
		Collection<ContractDataItem> ditems = citem.getDataItems();

		for (ContractDataItem di : ditems) {
			if (new Integer(month).equals(di.getMonth())) {
				ditem = di;
				break;
			}
		}
		if (ditem == null) {
			logger.error("当月合同数不存在!");
			return;
		}
		Double custom = ditem.getCustom();
		ditem.setCustom(custom + (added ? 1 : -1) * pin.getValue());
		getSession().save(ditem);
	}

	@Override
	public Contract getByName(String name) {
		Criteria crt = getSession().createCriteria(getType());
		crt.add(Property.forName("name").eq(name));
		Iterator itr = crt.list().iterator();
		return (Contract) (itr.hasNext() ? itr.next() : null);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public Collection<Contract> getsByClient(ContractClient client) {
		String hql = HQL(
				"select new Contract(c.id,c.name,c.dueDate) from %s c where c.client=:client",
				table());
		List list = query(hql).setParameter("client", client).list();
		return list;
	}

	@Override
	public void updateStorage(Contract cont, Product product, Double outcome) {
		if (product == null || cont == null || outcome == null)
			return;
		cont = get(cont.getId());
		Collection<ContractItem> contItems = cont.getContractItems();
		ContractItem citem = null;
		for (ContractItem item : contItems) {
			if (item == null)
				continue;
			if (product.equals(item.getOwner())) {
				citem = item;
				break;
			}
		}
		if (citem == null)
			throw new IllegalStateException(
					"合同数据在出库单填写前后发生变化，导致数据不一致，请重新刷新页面，选择合同");
		citem.setStorage(citem.getStorage() - outcome);
		getSession().update(citem);
	}

	@Override
	public Collection<ContractItem> search(ContractClient client) {
		String hql = HQL("from %s t where t.client.id='%s'", table(),
				client.getId());
		List list = query(hql).list();
		return list;
	}

	@Override
	public Collection<ContractItem> search(Product product) {
		String hql = HQL("from %s t where t.owner.code='%s' ",
				table(ContractItem.class), product.getCode());
		List list = query(hql).list();
		return list;
	}

	@Override
	public Collection<ContractItem> search(Department product) {
		String hql = HQL(
				"from %s t join t.owner.providers depts  where depts in (:depts)",
				table(ContractItem.class));
		Collection<Department> depts = Lists.newArrayList();
		depts.add(product);
		List list = query(hql).setParameterList("depts", depts).list();
		return list;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public Collection<ContractItem> search(ProductValidation validation) {
		String hql = HQL("from %s t where t.validation=:valid");
		List list = query(hql).setParameter("valid", validation).list();
		return list;
	}

	@Override
	public Collection<ContractItem> search(DateTime dt, int flag) {
		if (flag == 0)
			return this.searchForYear(dt.getYear());
		else if (flag == 1)
			return this.searchForQuota(dt);
		else if (flag == 2)
			return this.searchForMonth(dt.getMonthOfYear());

		return null;
	}

	private Collection<ContractItem> searchForYear(int year) {
		String hql = HQL("from %s t where year(t.contract.dueDate)=%d",
				table(ContractItem.class), year);
		List list = query(hql).list();
		return list;
	}

	private Collection<ContractItem> searchForMonth(int month) {
		String hql = HQL("from %s t where month(t.contract.dueDate)=%d",
				table(ContractItem.class), month);
		List list = query(hql).list();
		return list;
	}

	private Collection<ContractItem> searchForQuota(DateTime dt) {
		String hql = HQL(
				"from %s t where quarter(t.contract.dueDate)=quarter('%s')",
				table(ContractItem.class), dt.toString("yyyy-MM-dd"));
		List list = query(hql).list();
		return list;
	}

	@SuppressWarnings("rawtypes")
	@Override
	public Collection<String> getContractNames() {
		String hql=HQL("select distinct t.name from %s t",table());
		List list  =query(hql).list();
		return list;
	}

	private ContractItemService itemService;
	
	@Resource
	public void setItemService(ContractItemService itemService) {
		this.itemService = itemService;
	}

	@Override
	public Page query(Query qc, Integer start, Integer rows)
			throws DataAccessException {		
		return itemService.query(qc, start, rows);
	}
	
	

	@Override
	public Page details(Query qc, Integer pindex, Integer rows) {
			
		return itemService.details(qc, pindex, rows);
	}

	@Override
	public void removeContract(Integer year) {

	}

	@Override
	public Page collect(Query query) throws DataAccessException {
		return itemService.collect(query);
	}

	@Override
	public int countByExpress(Query query) {
		return itemService.countByExpress(query);
	}

}